/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package it.unitn.disi.wikireputation.main;

import it.unitn.disi.wikireputation.util.Author;
import it.unitn.disi.wikireputation.util.DBConnection;
import it.unitn.disi.wikireputation.util.Edit;
import it.unitn.disi.wikireputation.util.RepCalculator;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashMap;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 *
 * @author carlos
 */
public class Main {

    private static final int RANGECOUNT = 148;
    private static final double RANGESIZE = 148;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {


       
        String arg = args[0];

        if(arg.equals("--rpage")){
            computeReputationByPage();
        }else if(arg.equals("--rcat")){
            computeReputationByCategory();
        }else if(arg.equals("--rankFreq")){
            computeRankingsFrequentist();
        }else if(arg.equals("--rankFixedClass")){
            computeRankingsFixedClass();
        }else if(arg.equals("--authEditSurvival")){
            computeAuthorEditSurvival();
        }else if(arg.equals("--avgEditSurvivalAuthor")){
            computeAvgEditSurvivalAuthor();
        }


    }

    private static void computeReputationByPage(){
        try{

                String strSqlDom = " select distinct category_id from domain where category_id>=12649 order by category_id";
                PreparedStatement psDom = DBConnection.getConnection().prepareStatement(strSqlDom);

                ResultSet rsDom = psDom.executeQuery();

                String strSqlPage = " select page_id from domain where category_id = ?  order by rand() limit 200";
                PreparedStatement psPage = DBConnection.getConnection().prepareStatement(strSqlPage);


                String strSqlIns = " insert into reputation values(?, ?, ?, ?) ";
                PreparedStatement psIns = DBConnection.getConnection().prepareStatement(strSqlIns);

                String strSqlQry = " select page_id from reputation where page_id = ? ";
                PreparedStatement psQry = DBConnection.getConnection().prepareStatement(strSqlQry);


                while(rsDom.next()){
                    int catId = rsDom.getInt("category_id");
                    psPage.setInt(1, catId);

                    ResultSet rsPage = psPage.executeQuery();

                    while(rsPage.next()){
                        int pageId = rsPage.getInt("page_id");


                        try{
                            psQry.setInt(1, pageId);
                            ResultSet rsQry = psQry.executeQuery();

                            if(rsQry.next()){

                                //Do nothing. We already computed reputation for this page.

                            }else{

                                RepCalculator repCal = new RepCalculator();
                                repCal.authorReputationIncrement(pageId);
                                HashMap<Integer, Author> authorsRep = repCal.getAuthorsRep();

                                for(int userId : authorsRep.keySet()){
                                    Author author = authorsRep.get(userId);

                                    try{

                                        psIns.setInt(1, pageId);
                                        psIns.setInt(2, userId);
                                        psIns.setString(3, author.getAuthorName());
                                        psIns.setDouble(4, author.getReputation());
                                        psIns.execute();


                                    }catch(SQLException e){

                                        e.printStackTrace();

                                    }
                                }

                                System.out.println("(" + catId + "," + pageId + ")");
                            }

                        }catch(SQLException e){

                            e.printStackTrace();

                        }

                    }

                }



            }catch(Exception e){

                e.printStackTrace();

            }finally{

                DBConnection.closeConnection();

            }
    }

    private static void computeReputationByCategory(){
        try {

            String strSqlCat = " select distinct category_id from domain order by category_id";
            PreparedStatement psCat = DBConnection.getConnection().prepareStatement(strSqlCat);
            ResultSet rsCat = psCat.executeQuery();

            String strSqlPage = " select distinct d.page_id "+
                                " from domain d, reputation r "+
                                " where category_id = ? and d.page_id = r.page_id "+
                                " order by rand() limit 200 ";

            PreparedStatement psPage = DBConnection.getConnection().prepareStatement(strSqlPage);

            String strSqlRep = " select author_id, author_name, reputation from reputation where page_id = ? ";
            PreparedStatement psRep = DBConnection.getConnection().prepareStatement(strSqlRep);

            String strSqlIns = " insert into category_reputation values(?, ?, ?, ?, null) ";
            PreparedStatement psIns = DBConnection.getConnection().prepareStatement(strSqlIns);

            while(rsCat.next()){

                HashMap<Integer, LinkedList> authorReps = new HashMap<Integer, LinkedList>();
                HashMap<Integer, String> authorNames = new HashMap<Integer, String>();

                int catId = rsCat.getInt("category_id");

                psPage.setInt(1, catId);
                ResultSet rsPage = psPage.executeQuery();

                while(rsPage.next()){

                    int pageId = rsPage.getInt("page_id");
                    
                    psRep.setInt(1, pageId);
                    ResultSet rsRep = psRep.executeQuery();

                    double authorRep = RepCalculator.INITIALREP;
                    
                    while(rsRep.next()){
                        int authorId = rsRep.getInt("author_id");
                        double rep   = rsRep.getDouble("reputation");
                        String authorName = rsRep.getString("author_name");

                        LinkedList lstReps = authorReps.get(authorId);
                        authorNames.put(authorId, authorName);

                        if(lstReps == null){
                            lstReps = new LinkedList();
                            authorReps.put(authorId, lstReps);
                        }

                        lstReps.add(rep);
                    }                    

                }

                for(Integer author: authorReps.keySet()){
                    LinkedList<Double> lst = authorReps.get(author);
                    String authorName = authorNames.get(author);

                    int count = 0;
                    double sum = 0.0;
                    for(double rep: lst){
                        sum += rep;
                        count++;
                    }

                    psIns.setInt(1, catId);
                    psIns.setInt(2, author);
                    psIns.setString(3, authorName);
                    
                    if(count > 0){
                        psIns.setDouble(4, sum / (double)count);
                    }else{
                        psIns.setDouble(4, RepCalculator.INITIALREP);
                    }

                    psIns.execute();
                    

                }

            }


        } catch (Exception e) {

            e.printStackTrace();
            
        }
    }

    private static void computeRankingsFrequentist(){

        try{

            String strSqlCat = " select distinct category_id from category_reputation where category_id=57650 order by category_id";
            PreparedStatement psCat = DBConnection.getConnection().prepareStatement(strSqlCat);
            ResultSet rsCat = psCat.executeQuery();

            String strSqlFreq = " select count(*) as freq from category_reputation where category_id = ? and " +
                                " reputation >= ? and reputation < ? ";
            PreparedStatement psFreq = DBConnection.getConnection().prepareStatement(strSqlFreq);



            while(rsCat.next()){

                HashMap<Integer, Integer> rangeFreq = new HashMap<Integer, Integer>();

                int catId = rsCat.getInt("category_id");

                int bucket = 0;
                int totalFreq = 0;

                while(bucket <= RANGECOUNT){
                    psFreq.setInt(1, catId);
                    psFreq.setDouble(2, (double)bucket * RANGESIZE);
                    psFreq.setDouble(3, ((double)bucket+1) * RANGESIZE);

                    ResultSet rsFreq = psFreq.executeQuery();

                    int freq = 0;
                    if(rsFreq.next()){
                        freq = rsFreq.getInt("freq");
                    }

                    totalFreq += freq;
                    rangeFreq.put(bucket, freq);

                    bucket++;
                }



                int freqRangeSize = java.lang.Math.round(totalFreq / 4);


                Object objs[] = rangeFreq.keySet().toArray();
                Arrays.sort(objs);


                int freqCumul = 0;

                int rank1 = -1;
                int rank2 = -1;
                int rank3 = -1;

                

                for(Object obj : objs){
                    int freq = rangeFreq.get((Integer)obj);

                    freqCumul += freq;

                    if(freqCumul >= freqRangeSize){
                        freqCumul = 0;

                        if(rank1 == -1){
                            rank1 = (Integer)obj;
                        }else if(rank2 == -1){
                            rank2 = (Integer)obj;
                        }else if(rank3 == -1){
                            rank3 = (Integer)obj;
                        }
                    }
                    
                }

                System.out.println("Rank1: " + rank1);
                System.out.println("Rank2: " + rank2);
                System.out.println("Rank3: " + rank3);



                /*
                Object objs[] = rangeFreq.keySet().toArray();

                Arrays.sort(objs);

                for(Object obj : objs){
                    int freq = rangeFreq.get((Integer)obj);

                    System.out.println(obj + ": " + freq);
                }
                 */
                

            }

            

        }catch(Exception e){
            
            e.printStackTrace();
            
        }


    }

    private static void computeRankingsFixedClass(){

        try{

            String strSqlMinMax = " select min(reputation) as minrep, max(reputation) as maxrep " +
                                  " from category_reputation " +
                                  " where category_id = ? ";
            PreparedStatement psMinMax = DBConnection.getConnection().prepareStatement(strSqlMinMax);
            //ResultSet rsMinMaz = psCat.executeQuery();

            String strSqlCat = " select distinct category_id from category_reputation ";
            PreparedStatement psCat = DBConnection.getConnection().prepareStatement(strSqlCat);
            ResultSet rsCat = psCat.executeQuery();

            while(rsCat.next()){

                int catId = rsCat.getInt("category_id");

                psMinMax.setInt(1, catId);
                ResultSet rsMinMax = psMinMax.executeQuery();

                double minRep = 0.0;
                double maxRep = 0.0;

                if(rsMinMax.next()){
                    minRep = rsMinMax.getDouble("minrep");
                    maxRep = rsMinMax.getDouble("maxRep");
                }

                double rangeRep = maxRep - minRep;
                double classWidth = rangeRep / 4;

                double lim0 = 0.0;
                double lim1 = classWidth;
                double lim2 = classWidth * 2;
                double lim3 = classWidth * 3;
                double lim4 = 22026.0 + 1; //max with correction

                String strIns = " update category_reputation " +
                                " set rank = ? " +
                                " where category_id = ? and reputation >= ? and reputation < ? ";
                PreparedStatement psIns = DBConnection.getConnection().prepareStatement(strIns);

                psIns.setInt(1, 1);
                psIns.setInt(2, catId);
                psIns.setDouble(3, lim0);
                psIns.setDouble(4, lim1);
                psIns.execute();

                psIns.setInt(1, 2);
                psIns.setInt(2, catId);
                psIns.setDouble(3, lim1);
                psIns.setDouble(4, lim2);
                psIns.execute();

                psIns.setInt(1, 3);
                psIns.setInt(2, catId);
                psIns.setDouble(3, lim2);
                psIns.setDouble(4, lim3);
                psIns.execute();

                psIns.setInt(1, 4);
                psIns.setInt(2, catId);
                psIns.setDouble(3, lim3);
                psIns.setDouble(4, lim4);
                psIns.execute();                

            }


        }catch(Exception e ){

            e.printStackTrace();

        }

    }

    private static void computeAuthorEditSurvival(){

        try{

            RepCalculator repCal = new RepCalculator();

            String strSqlPages = " select distinct page_id from reputation order by page_id ";
            PreparedStatement psPages = DBConnection.getConnection().prepareStatement(strSqlPages);
            ResultSet rsPages = psPages.executeQuery();

            String strSqlFirstLastRev = " select min(rev_id) firstrevision, max(rev_id) as lastrevision from revision where rev_page = ? ";
            PreparedStatement psFirstLastRev = DBConnection.getConnection().prepareStatement(strSqlFirstLastRev);

            String strSqlIns = " insert into edit_survival values(?, ?, ?, ?, ?, null, null) ";
            PreparedStatement psIns = DBConnection.getConnection().prepareStatement(strSqlIns);

            String strSqlRev =  " SELECT rev_id, rev_text_id, rev_user_text, rev_user, old_text " +
                                " FROM revision r, text t " +
                                " WHERE r.rev_text_id = t.old_id and rev_page = ? " +
                                " ORDER BY rev_id asc ";
            PreparedStatement psRev = DBConnection.getConnection().prepareStatement(strSqlRev);

            while(rsPages.next()){

                HashMap<Integer, LinkedList> authEditSurvival = new HashMap<Integer, LinkedList>();
                HashMap<Integer, String> authorNames = new HashMap<Integer, String>();

                int pageId = rsPages.getInt("page_id");

                psRev.setInt(1, pageId);
                ResultSet rsRev = psRev.executeQuery();

                if(!rsRev.next()){
                    continue;
                }

                rsRev.last();
                int lastRevId = rsRev.getInt("rev_id");
                int revCount = rsRev.getRow();
                java.sql.Blob blob_j = rsRev.getBlob("old_text");
                byte[] bdata_j = blob_j.getBytes(1, (int) blob_j.length());
                String strLastRev = new String(bdata_j);

                rsRev.first();

                int rev_id_i_1 = rsRev.getInt("rev_id");
                int auth_i_1 = rsRev.getInt("rev_user");
                java.sql.Blob blob_i_1 = rsRev.getBlob("old_text");
                byte[] bdata_i_1 = blob_i_1.getBytes(1, (int) blob_i_1.length());
                String strRev_i_1 = new String(bdata_i_1);

                while(rsRev.getRow() < revCount){

                    while(rsRev.next()){
                        if(rsRev.getInt("rev_user") != auth_i_1){
                            break;
                        }

                        //rsRev.previous();
                        rev_id_i_1 = rsRev.getInt("rev_id");
                        auth_i_1 = rsRev.getInt("rev_user");
                        blob_i_1 = rsRev.getBlob("old_text");
                        bdata_i_1 = blob_i_1.getBytes(1, (int) blob_i_1.length());
                        strRev_i_1 = new String(bdata_i_1);

                        //rsRev.next();

                        //System.out.println("rev_id: " + rev_id_i_1);

                    }

                    int rev_id_i = rsRev.getInt("rev_id");
                    int auth_i = rsRev.getInt("rev_user");
                    String auth_i_name = rsRev.getString("rev_user_text");
                    java.sql.Blob blob_i = rsRev.getBlob("old_text");
                    byte[] bdata_i = blob_i.getBytes(1, (int) blob_i.length());
                    String strRev_i = new String(bdata_i);

                    while(rsRev.next()){
                        if(rsRev.getInt("rev_user") != auth_i){
                            break;
                        }

                        //rsRev.previous();
                        rev_id_i = rsRev.getInt("rev_id");
                        auth_i = rsRev.getInt("rev_user");
                        auth_i_name = rsRev.getString("rev_user_text");
                        blob_i = rsRev.getBlob("old_text");
                        bdata_i = blob_i.getBytes(1, (int) blob_i.length());
                        strRev_i = new String(bdata_i);


                        //rsRev.next();
                        //System.out.println("rev_id: " + rev_id_i_1);
                    }


                    //We get the edit longevity, a value in [-1..1]
                    Edit edit = new Edit(0, 0.0);
                    repCal.getEditSurvival(strRev_i_1, strRev_i, strLastRev, edit);

                    if((edit.getEditLongevity() >= -1.0) && (edit.getEditLongevity() <= 1.0)){

                        //We take the eLong from [-1..1] to [0..1]
                        double eLongScaled = (1 + edit.getEditLongevity()) / 2;

                        edit.setEditLongevity(eLongScaled);

                        authorNames.put(auth_i, auth_i_name);
                        LinkedList lstELong = authEditSurvival.get(auth_i);
                        if( lstELong == null)
                            lstELong = new LinkedList();

                        lstELong.add(edit);
                        authEditSurvival.put(auth_i, lstELong);

                    }

                    rev_id_i_1 = rev_id_i;
                    auth_i_1 = auth_i;
                    blob_i_1 = blob_i;
                    bdata_i_1 = bdata_i;
                    strRev_i_1 = strRev_i;

                    rsRev.previous();

                    //System.out.println("(PageId,RevisionId): (" + pageId +  "," + rev_id_i_1 +")");
                }

                for(Integer author : authEditSurvival.keySet()){
                    LinkedList list = authEditSurvival.get(author);
                    String authorName = authorNames.get(author);

                    double sumEditLong = 0.0;
                    int sumContrib = 0;
                    for(Object edit: list){
                        sumEditLong = sumEditLong + ((Edit)edit).getEditLongevity();
                        sumContrib = sumContrib + ((Edit)edit).getContribSize();
                    }

                    double avgEditLong = sumEditLong / (double)list.size();

                    psIns.setInt(1, pageId);
                    psIns.setInt(2, author);
                    psIns.setString(3, authorName);
                    psIns.setDouble(4, avgEditLong);
                    psIns.setInt(5, sumContrib);
                    psIns.execute();

                    System.out.println("(PageId, AuthorId, Rate, EditSum) =  (" + pageId + ", " + author + ", " + avgEditLong + ", " + sumContrib + ")");
                }
               
                               
            }
            
            
            

        }catch(Exception e){

            e.printStackTrace();

        }
    }

    private static void computeAvgEditSurvivalAuthor(){

        try{

            String strSqlUpdate = " update edit_survival set avg_edit_survival = edit_sum_author * edit_survival ";
            PreparedStatement psUpdate = DBConnection.getConnection().prepareStatement(strSqlUpdate);

            psUpdate.execute();
            
        }catch(Exception e){

            e.printStackTrace();
            
        }
    }

}
